//============================================================================
// Name        : parameter.c
// Author      : Yugi
// Version     : 1.0
// Description : 
//============================================================================

#include <stdio.h>
#include "parameter.h"

PPROPERTY properties;

/*
* Get option from string 'options' according to 'optcode'
*
* REQUIRE:	options not NULL
*			optcode can be any inputable character but ' ' followed by -
* RETURN:	option from string 'options' according to 'optcode', NULL if not found
* EXAMPLE:	INPUT	"-aFoo -bBar", 'b'
*			OUTPUT	"Bar"
* NOTES:	Return value should be freed by user
*/
char* pa_option(const char* options, char optcode){
	char buf[BUF_SIZE];	// Buffer for copying options
	char *token;		// Temp pointer for token
	/* malloc some memory for return  */
	char *ret = (char *) malloc(BUF_SIZE);

	/* Make a copy of options since we will destruct it by strtok */
	strncpy(buf, options, BUF_SIZE);
	
	/* spilt options into tokens and test one by one till matching */
	token = strtok(buf," ");

	while(token){
		if (*token=='-' && *(token+1)==optcode){
			strcpy(ret, token+2);
			return ret;
		}

		token = strtok(NULL, " ");
	}

	return NULL;
}

/*
* Parse the file 'input' and save the key-value pair into
* properties.
*
* REQUIRE:	input not NULL
*			<file format>
*				key1 = content1
*				key2 = content2
*				EOF
* EXAMPLE:	INPUT	<file content>
*					key1 = content1
*					key2 = content2
*					EOF
*/
void pa_file(file_t input){
	char fBuf[BUF_SIZE];// Buffer for file content
	char *kBuf;			// Buffer for key
	char *vBuf;			// Buffer for value
	char *pStr;			// Temporary string pointer
	int current;		// Current char
	bool fRead = true;	// Read flag

	while (fRead){
		/* Read from input */
		memset(fBuf, 0, BUF_SIZE);

		if (dr_read_file(input, fBuf, BUF_SIZE) <= 0)
			return;

		/* initial iterator */
		pStr = fBuf;
		current = 0;

		while (current<BUF_SIZE && fRead){
			switch (*(fBuf+current)){
				case '\0':
					/* EOF */
					fRead = false;
					break;
				case '\n':
					/* Line */
					*(fBuf+current) = '\0';
					kBuf = (char*) malloc(KEY_SIZE);
					vBuf = (char*) malloc(VALUE_SIZE);
					memset(kBuf, 0, KEY_SIZE);
					memset(vBuf, 0, VALUE_SIZE);

					/* Match key-value pair	*/
					if (sscanf(pStr, "%[^ ,=]%*[ ,=]%s", kBuf, vBuf) != 2)
						return;
					else{
						/* Save to properties */
						PPROPERTY temp = (PPROPERTY) malloc(sizeof(PROPERTY));
						temp->key = kBuf;
						temp->value = vBuf;
						temp->next = NULL;
						
						if (properties)
							temp->next = properties;

						properties = temp;
					}

					pStr = fBuf+current+1;
				default:
					current++;
			}
		}

		/* File size greater than BUF_SIZE, then seek to the last line we read */
		if (!dr_file_seek(input, BUF_SIZE-(pStr-fBuf), DR_SEEK_CUR))
			return;
	}
}

/*
* Get property according to key
*
* REQUIRE:	key not NULL
* RETURN:	value that matches key
*/
char *pa_property(char *key){
	PPROPERTY pItr = properties;

	/* Iterate through properties */
	while (pItr){
		if (!strcmp(pItr->key, key))
			return pItr->value;

		pItr = pItr->next;
	}

	return NULL;
}

/*
* This method compares int-value instead of char*. To match "%esp", 
* we only have to translate it to 'p''s''e''%' (0x70736525).Note
* that it only works right on little endian machines, and for now  
* only %eax, %ecx, %edx, %ebx, %esp, %ebp, %esi, %edi can be 
* recognized.
*
* REQUIRE:	src not NULL
* RETURN:	register id
* EXAMPLE:	INPUT	%esp
*			OUTPUT	REG_ESP
*/
reg_id_t pa_register(char *src){
	switch (*((int *) src)){
		/* REG_EAX */
		case 0x58414525:
		case 0x78616525:
			return REG_EAX;
		/* REG_ECX */
		case 0x58434525:
		case 0x78636525:
			return REG_ECX;
		/* REG_EDX */
		case 0x58444525:
		case 0x78646525:
			return REG_EDX;
		/* REG_EBX */
		case 0x58424525:
		case 0x78626525:
			return REG_EBX;
		/* REG_ESP */
		case 0x50534525:
		case 0x70736525:
			return REG_ESP;
		/* REG_EBP */
		case 0x50424525:
		case 0x70626525:
			return REG_EBP;
		/* REG_ESI */
		case 0x49534525:
		case 0x69536525:
			return REG_ESI;
		/* REG_EDI */
		case 0x49444525:
		case 0x69646525:
			return REG_EDI;
		default:
			return REG_NULL;
	}
}

/*
* Encode operand. Now it support two kinds of operands: registers 
* and base-disp operands. 
*
* REQUIRE:	src not NULL
* RETURN:	operand encoded using 'src'. Empty operand if we cannot
*			parse it.
*/
opnd_t pa_operand(char *src){
	/* operand is register */
	if (*src == '%' && strlen(src)==4)
		return opnd_create_reg(pa_register(src));

	/* operand is base-disp	*/
	/* 1) case "(%e--)" */
	if (*src == '(')
		return opnd_create_base_disp(pa_register(src+1), REG_NULL, 0, 0, 0x90);

	if (*src >= '0' && *src <= '9'){
		char base_buf[BUF_SIZE];
		int disp = 0;
		int ret = sscanf(src, "%x%s", &disp, base_buf);

		switch (ret){
	/* 2) case "0x--" */
			case 1:
				return opnd_create_base_disp(REG_NULL, REG_NULL, 0, disp, 0x90);
	/* 3) case "0x--(%e--)" */
			case 2:
				return opnd_create_base_disp(pa_register(base_buf+1), REG_NULL, 0, disp, 0x90);
			default:
				return opnd_create_base_disp(REG_NULL, REG_NULL, 0, 0, 0x90);
		}
	}
		return opnd_create_base_disp(pa_register(src+1), REG_NULL, 0, 0, 0x90);

	return opnd_create_reg(REG_NULL);
}

/*
* Encode operands using pa_operand() and store them in worklist.
*
* REQUIRE:	src not NULL
* RETURN:	worklist that contains operands encoded from src.
*/
PWORKLIST pa_operands(char *src){
	PWORKLIST pList = wl_new();	// worklist to store operands
	char buf[BUF_SIZE];			// buffer for making copy of src
	char *token;				// temp pointer for token

	strncpy(buf, src, BUF_SIZE);
	
	/* spilt src into tokens and encode them one by one */
	token = strtok(buf, ",");

	while(token){
		wl_gen(pList, pa_operand(token));
		token = strtok(NULL, ",");
	}

	return pList;
}
